package com.wnn.demo.shiro.filter;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import com.wnn.demo.advice.MyException;
import com.wnn.demo.domain.Role;
import com.wnn.demo.domain.User;
import com.wnn.demo.model.system.mapper.UserMapper;
import com.wnn.demo.pojo.ResultCode;
import com.wnn.demo.utils.redis.RedisManager;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * @description: 登陆次数限制
 */
public class RetryLimitHashedCredentialsMatcher extends HashedCredentialsMatcher {

    private static final Logger logger =  LoggerFactory.getLogger(RetryLimitHashedCredentialsMatcher.class);

    public static final String DEFAULT_RETRYLIMIT_CACHE_KEY_PREFIX = "shiro:cache:retrylimit:";
    private String keyPrefix = DEFAULT_RETRYLIMIT_CACHE_KEY_PREFIX;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RedisManager redisManager;

    public void setRedisManager(RedisManager redisManager) {
        this.redisManager = redisManager;
    }

    private String getRedisKickoutKey(String username) {
        return this.keyPrefix + username;
    }

    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        //获取用户名
        String username = (String)token.getPrincipal();
        User user = userMapper.findUserHaveRolesByUserName(username);
        List< Role > roles = user.getRoles();
        //如果是超级管理员不做锁定处理
        boolean isAdministrator =false;
        if (roles!=null){
            for (Role r:roles
                 ) {
                if ("administrator".equals(r.getName())){
                    isAdministrator =true;
                }
            }
        }
        //获取用户登录次数
        AtomicInteger retryCount = (AtomicInteger)redisManager.get(getRedisKickoutKey(username));
        if (retryCount == null) {
            //如果用户没有登陆过,登陆次数加1 并放入缓存
            retryCount = new AtomicInteger(0);
        }
        //普通用户和管理员会锁定
        if ( (retryCount.incrementAndGet() > 5 ) && !isAdministrator) {
            //如果用户登陆失败次数大于5次 抛出锁定用户异常  并修改数据库字段
            if (user != null &&  user.getStatus()==1 ){
                //数据库字段 默认为 0  就是正常状态 所以 要改为1
                //修改数据库的状态字段为锁定
                user.setStatus(0);
                userMapper.updateByPrimaryKey(user);
            }
            logger.info("锁定用户" + user.getUserName());
            //抛出用户锁定异常
            throw new MyException(ResultCode.FALSE,"账号异常，请联系管理员！");
        }
        //判断用户账号和密码是否正确
        // 两个参数 token是控制层用户Login过来的凭证,token中的密码是明文 info是reaml封装的Info信息，info中的密码是md5加密的密码
        //明文和密文比较
        boolean matches = super.doCredentialsMatch(token, info);
        if (matches && !isAdministrator) {
            //如果正确,从缓存中将用户登录计数 清除
            redisManager.del(getRedisKickoutKey(username));
        }else {
            // 这里创建了缓存shiro:cache:retrylimit:username
            redisManager.set(getRedisKickoutKey(username), retryCount);
        }
        return matches;
    }

    /**
     * 根据用户名 解锁用户
     * @param username
     * @return
     */
    public void  unlockAccount(String username){
        User user = userMapper.findUserByName(username);
        if (user != null){
            //修改数据库的状态字段为锁定
            user.setStatus(1);
            userMapper.updateByPrimaryKey(user);
            redisManager.del(getRedisKickoutKey(username));
        }
    }

}